
TableUtil = require("SharedLibs.Utils.TableUtil");
MathUtil = require("SharedLibs.Math.MathUtil");
FP = require("SharedLibs.FP.Function");

local Array = {}
 
function Array.new()
	local arr = {};
	FP.extends(arr, Array);
	return arr;
end

function Array.FromTable(t, keyFunc)
	local arr = TableUtil.ToArray(t, keyFunc);
	FP.extends(arr, Array);
	return arr;
end

--- 把一个table转换成一个Array对象
function Array.Attach(arr)	
	FP.extends(arr, Array);
	return arr;
end

--- 随机取一个数
function Array:RandGet()
	return self[math.random(1, #self)];
end

function Array:Contains(item)
	for index, value in ipairs(self) do
		if value == item then
			return true
		end
	end
	return false
end

function Array:Clear()
	local count = #self;
	for i=0, count do self[i]=nil end
end

--- 洗牌
function Array:Shuffle()
	MathUtil.Shuffle(self);
end

--- Add items to the end of an array
Array.Push = table.insert;
--- Remove an item from the end of an array
Array.Pop = table.remove;

--- Remove an item from the beginning
function Array:Shift()
	return table.remove(self, 1)
end

--- Insert an item into the beginning
function Array:Unshift(value)
	return table.insert(self, 1, value)
end

Array.RemoveHead = Array.Shift;
Array.InsertHead = Array.Unshift;

Array.RemoveTail = Array.Push;
Array.InsertTail = Array.Pop;

--- Insert an item into the any valid position
Array.Insert = table.insert;

Array.Remove = table.remove;
function Array:Remove(pos)
	table.remove(self, pos);
end

function Array:RemoveItem(item)
	for index, value in ipairs(self) do
		if value == item then
			return table.remove(self, index);
		end
	end
	return nil;
end

--- If 'check' return true, will be removed
--- Any item who meets the condition will be removed.
function Array:RemoveBy(check)
	for index, value in ipairs(self) do
		if check(value) then
			return table.remove(self, index);
		end
	end
	return nil;
end

--- 找到第一个符合条件的item, 返回item和index
--- 如果没有符合条件的返回nil
function Array:FindItem(predicate)
	for index, value in ipairs(self) do
		if predicate(value, index) then
			return value, index;
		end
	end
	return nil;
end

function Array:ForEach(func)
	for i, value in ipairs(self) do
		func(value, i);
	end
end

function Array:BackForEach(func)
	for i=#self, 1, -1 do
		func(self[i]);
	end
end

Array.Sort = table.sort;
function Array:Sort(func)
	return table.sort(self, func);
end

function Array:Reverse()
    local i = 1;
    local j = #self;

    while i < j do
        self[i], self[j] = self[j], self[i]
        i = i + 1
        j = j - 1
    end
	return self;
end

--- 转变成一个lua默认Array(没有任何方法, 方便使用PrintTable打印)
function Array:ToPlainArray()
	local newArr = {};
	for _, value in ipairs(self) do
		table.insert(newArr, value);
	end
	return newArr;
end

--- 每次table.remove都会导致re-index整个数组（quadratic number of moves will be performed）
--- BatchRemove提供一个fnKeep用来检测一个元素是否需要保留，一次性把所有需要移除的元素都移除。
--- fnKeep有三个参数(t, i, j)，t就是当前数组，i是当前索引，j是目标索引（移动后的索引）
--- 如果fnKeep返回false，那么这个元素就会被移除。
--- Ref: https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating
---
--- Note: 这个函数会改变元素索引，所以，如果索引很重要，切记要在fnKeep里使用j来更新索引！！！
function Array:BatchRemove(fnKeep)
	local t = self;
	local j, n = 1, #t;

	for i=1,n do
		if (fnKeep(t, i, j)) then
			-- Move i's kept value to j's position, if it's not already there.
			if (i ~= j) then
				t[j] = t[i];
				t[i] = nil;
			end
			j = j + 1; -- Increment position of where we'll place the next kept value.
		else
			t[i] = nil;
		end
	end
end

return Array



